/**
 * Vivien2
 *
 * Copyright (c) 2009 Alexei (aka Lancelot) Samsonov.
 * E-mail: alexey.v.samsonov@gmail.com; 
 *
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */


/**
 * Authors: Alexei Samsonov (aka Lancelot), alexey.v.samsonov@gmail.com
 */

/**
 * Date: 
 */


module obj.macros;

import std.string;
import std.regexp;

import logger;

// --- Убрать после тестов ---
import std.stdio;


class Macros
{
	private char[] _string;
	// Массив макросов, внутри которых содержаться другие макросы.
	private char[][char[]] _mtree;
	// Массив макросов, внутри которых содержится только текст.
	private char[][char[]] _mleaf;

	
	this(char[] s)
	{
		_string = "{main}\n" ~ s;
	}

	

	char[] parse()
	{
		char[][] lines;
		char[] mname;		// Имя найденного макроса.
		char[] pattern;
		


		/**
		 * Этап 1.
		 * -------
		 * Поиск объявлений и сохранение макросов.
		 */
		lines = splitlines(_string);
		pattern = "^[ \t]*[[]([a-zA-Z][a-zA-Z0-9_]*)[]][ \t]*$";
		_string = "";


		foreach(char[] s; lines)
		{
			// Строка с заголовком макроса.
			if(auto m = std.regexp.search(s , pattern))
			{
				mname = m.match(1);
				// Каждый следующий блок переопределяет предыдущий с тем же именем.
				_mtree[mname] = "";

				continue;
			}
		

			// Строка с телом макроса.
			if(mname != "")
			{
				_mtree[mname] ~= s ~ "\n"; 
			}
			

			// Строка обычного текста.
			else
			{
				_string ~= s ~ "\n";
			}
		}



		/**
		 * Этап 2.
		 * -------
		 * В массиве _mtree оставляем макросы, которые содержат другие макросы.
		 * В массив _mleaf переносим макросы, содержащие простой текст.
		 */
		while(macrosSort())
		{
			foreach(char[] n, char[] v; _mtree)
			{
				_mtree[n] = macrosReplace(v);
			}
		}



		/*
		 * Этап 3.
		 * -------
		 * Замена макросов в тексте.
		 */
		_string = macrosReplace(_string);



		// Если остались макросы с назаменёнными точками вставки, то пишем в лог.
		if(_mtree.length != 0)
		{
			char[] undef;
			undef = std.string.join(_mtree.keys, ", ");

			Logger log = new Logger();
			log.put("Warning", "Undefined macros: " ~ undef ~ ".");
			
		}



		return _string;
	}



	char[] macrosReplace(char[] s)
	{
		char[] pattern;
		char[]* p;

		pattern = "{([a-zA-Z][a-zA-Z0-9_]*)}";

		s = sub(s, pattern, 
				delegate char[] (RegExp m)
				{
					p = (m.match(1) in _mleaf);
					if(p == null)
						return m.match(0);

					return _mleaf[m.match(1)];
				},
				"g");
	
		return s;
	}



	/*****
	 * Обрабатывает массив _mtree, и макросы, не содержащие точек вставок 
	 * других макросов переносит в массив _mleaf.
	 * Если сортировка выполнена, т. е. в массиве _mtree найдены макросы,
	 * содержащие только текст, то функция возвращает true.
	 * Если сортировка не выполнена, то возвращается false.
	 */
	bool macrosSort()
	{
		char[] pattern = "{([a-zA-Z][a-zA-Z0-9_]*)}";
		bool is_sorted = false;
		char[][char[]] mtree_copy;


		foreach(char[] n, char[] v; _mtree)
		{
			if(std.regexp.find(v, pattern) == -1)
			{
				_mleaf[n] = v;
				is_sorted = true;
			}
			else
			{
				mtree_copy[n] = v;
			}
		}

		_mtree = mtree_copy;


		
		return is_sorted;
	}

}
